package templatemethod

import (
	"fmt"
	flag "github.com/spf13/pflag"
	"os"
)

// 详细参考: https://blog.csdn.net/pmlpml/article/details/78326769
// 设计模式之美：Template Method（模板方法）[http://www.cnblogs.com/gaochundong/p/design_pattern_template_method.html]

//关键在于：

//抽象操作（Primitive Operation）(must be overridden)
//钩子操作（Hook Operation）(may be overridden)，通常提供默认实现
type Commander interface {
	Execute() error
}

// 饿汉模式
var Args = os.Args

type Command struct {
	// Use is the one-line usage message.
	Use string
	// Short is the short description shown in the 'help' output.
	Short string
	// Long is the long message shown in the 'help <this-command>' output.
	Long string
	// SetOptions:
	SetOptions func(c *Command) error
	// Parse:
	Parse func(c *Command) error
	// Run: Typically the actual work function. Most commands will only implement this.
	Run func(cmd *Command, args []string)

	flags *flag.FlagSet
}

// 定义了三个回调函数，也就是钩子操作。没有虚方法也是有办法的！

// Execute .
// 定义了模板方法 Execute()
func (c *Command) Execute() error {
	if ok := c.SetOptions(c); ok != nil {
		fmt.Println("Error in SetOptions!")
		return ok
	}
	if ok := c.Parse(c); ok != nil {
		fmt.Println("Error in Parsing!")
		return ok
	}
	c.Run(c, Args)
	return nil
}

// Flags returns the complete FlagSet that applies
// to this command (local and persistent declared here and by all parents).
func (c *Command) Flags() *flag.FlagSet {
	if c.flags == nil {
		c.flags = flag.NewFlagSet(c.Use, flag.ContinueOnError)
	}
	return c.flags
}

// todo :
//  go run templateMethods.go -uZhangSan
//  go run templateMethods.go --username=Zhangsan

func main() {
	var RootCmd = &Command{
		Use:   "test",
		Short: "A brief description of your application",
		Long:  "A longer description",
	}
	RootCmd.SetOptions = func(c *Command) error {
		fmt.Println("Set Options here")
		c.Flags().StringP("user", "u", "Anonymous", "Help message for username")
		return nil
	}
	RootCmd.Parse = func(c *Command) error {
		fmt.Println("Parse here")
		c.Flags().Parse(Args)
		return nil
	}
	RootCmd.Run = func(c *Command, a []string) {
		username, _ := c.Flags().GetString("user")
		fmt.Println("myCommand called by " + username)
	}
	if err := RootCmd.Execute(); err != nil {
		panic(err)
	}
}
